package org.apache.ocean.main.util;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.BooleanClause.Occur;

public class QueryUtil {

	/**
	 * Recursively walks the "from" query pulling out sub-queries and adding them
	 * to the "to" query.
	 * 
	 * <p>
	 * Boosts are multiplied as needed. Sub-BooleanQueryies which are not optional
	 * will not be flattened. From will be mangled durring the walk, so do not
	 * attempt to reuse it.
	 * </p>
	 */
	public static void flattenBooleanQuery(BooleanQuery to, BooleanQuery from) {
		for (BooleanClause clause : (List<BooleanClause>) from.clauses()) {
			Query cq = clause.getQuery();
			cq.setBoost(cq.getBoost() * from.getBoost());
			if (cq instanceof BooleanQuery && !clause.isRequired() && !clause.isProhibited()) {
				/* we can recurse */
				flattenBooleanQuery(to, (BooleanQuery) cq);
			} else {
				to.add(clause);
			}
		}
	}

	public static void setMinShouldMatch(BooleanQuery booleanQuery, String spec) {
		int optionalClauses = 0;
		for (BooleanClause booleanClause : (List<BooleanClause>) booleanQuery.clauses()) {
			if (booleanClause.getOccur() == Occur.SHOULD) {
				optionalClauses++;
			}
		}
		int msm = calculateMinShouldMatch(optionalClauses, spec);
		if (0 < msm) {
			booleanQuery.setMinimumNumberShouldMatch(msm);
		}
	}

	public static int calculateMinShouldMatch(int optionalClauseCount, String spec) {
		int result = optionalClauseCount;
		if (-1 < spec.indexOf("<")) {
			/* we have conditional spec(s) */
			for (String s : spec.trim().split(" ")) {
				String[] parts = s.split("<");
				int upperBound = (new Integer(parts[0])).intValue();
				if (optionalClauseCount <= upperBound) {
					return result;
				} else {
					result = calculateMinShouldMatch(optionalClauseCount, parts[1]);
				}
			}
			return result;
		}
		/* otherwise, simple expresion */
		if (-1 < spec.indexOf("%")) {
			/* percentage */
			int percent = new Integer(spec.replace("%", "")).intValue();
			float calc = (result * percent) / 100f;
			result = calc < 0 ? result + (int) calc : (int) calc;
		} else {
			int calc = (new Integer(spec)).intValue();
			result = calc < 0 ? result + calc : calc;
		}
		return (optionalClauseCount < result ? optionalClauseCount : (result < 0 ? 0 : result));
	}

	public static Map<String, Float> parseFieldBoosts(String[] fieldLists) {
		if (null == fieldLists || 0 == fieldLists.length) {
			return new HashMap<String, Float>();
		}
		Map<String, Float> out = new HashMap<String, Float>(7);
		for (String in : fieldLists) {
			if (null == in || "".equals(in.trim()))
				continue;
			String[] bb = in.trim().split("\\s+");
			for (String s : bb) {
				String[] bbb = s.split("\\^");
				out.put(bbb[0], 1 == bbb.length ? null : Float.valueOf(bbb[1]));
			}
		}
		return out;
	}

	public static CharSequence partialEscape(CharSequence s) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			if (c == '\\' || c == '!' || c == '(' || c == ')' || c == ':' || c == '^' || c == '[' || c == ']' || c == '{' || c == '}' || c == '~' || c == '*' || c == '?') {
				sb.append('\\');
			}
			sb.append(c);
		}
		return sb;
	}

	/**
	 * Returns it's input if there is an even (ie: balanced) number of '"'
	 * characters -- otherwise returns a String in which all '"' characters are
	 * striped out.
	 */
	public static CharSequence stripUnbalancedQuotes(CharSequence s) {
		int count = 0;
		for (int i = 0; i < s.length(); i++) {
			if (s.charAt(i) == '\"') {
				count++;
			}
		}
		if (0 == (count & 1)) {
			return s;
		}
		return s.toString().replace("\"", "");
	}
}
